package edu.ynu.se.xiecheng.achitectureclass.service;

import edu.ynu.se.xiecheng.achitectureclass.constant.SensorConstants;
import edu.ynu.se.xiecheng.achitectureclass.dto.SensorDataResponse;
import edu.ynu.se.xiecheng.achitectureclass.entity.*;
import edu.ynu.se.xiecheng.achitectureclass.exception.BusinessException;
import edu.ynu.se.xiecheng.achitectureclass.dao.GreenhouseRepository;
import edu.ynu.se.xiecheng.achitectureclass.dao.SensorDataRepository;
import edu.ynu.se.xiecheng.achitectureclass.dao.AirTemperatureRepository;
import edu.ynu.se.xiecheng.achitectureclass.dao.AirHumidityRepository;
import edu.ynu.se.xiecheng.achitectureclass.dao.SoilTemperatureRepository;
import edu.ynu.se.xiecheng.achitectureclass.dao.SoilPhRepository;
import edu.ynu.se.xiecheng.achitectureclass.dao.SoilNitrogenRepository;
import edu.ynu.se.xiecheng.achitectureclass.dao.SoilPhosphorusRepository;
import edu.ynu.se.xiecheng.achitectureclass.dao.SoilPotassiumRepository;
import edu.ynu.se.xiecheng.achitectureclass.dao.DeviceRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.util.stream.Collectors;
import java.util.HashMap;
import java.util.ArrayList;

@Slf4j
@Service
@RequiredArgsConstructor
public class SensorDataService {
    private final RestTemplate restTemplate;
    private final ApiTokenService apiTokenService;
    private final SensorDataRepository sensorDataRepository;
    private final GreenhouseRepository greenhouseRepository;
    private final AirTemperatureRepository airTemperatureRepository;
    private final AirHumidityRepository airHumidityRepository;
    private final SoilTemperatureRepository soilTemperatureRepository;
    private final SoilPhRepository soilPhRepository;
    private final SoilNitrogenRepository soilNitrogenRepository;
    private final SoilPhosphorusRepository soilPhosphorusRepository;
    private final SoilPotassiumRepository soilPotassiumRepository;
    private final DeviceRepository deviceRepository;
    
    // 使用Guava Cache来实现访问频率限制
    private final Cache<String, LocalDateTime> requestTimeCache = CacheBuilder.newBuilder()
            .expireAfterWrite(SensorConstants.ApiConstants.REQUEST_COOLDOWN_SECONDS, TimeUnit.SECONDS)
            .build();
    
    /**
     * 获取大棚的所有传感器数据
     * @return 传感器数据列表
     */
    public List<SensorDataResponse> getGreenhouseSensorData() {
        // 检查访问频率
        String cacheKey = "greenhouse_" + SensorConstants.GREENHOUSE_BOX_NO;
        LocalDateTime lastRequestTime = requestTimeCache.getIfPresent(cacheKey);
        if (lastRequestTime != null) {
            throw new BusinessException("访问频率过高，请稍后再试");
        }
        
        // 记录本次访问时间
        requestTimeCache.put(cacheKey, LocalDateTime.now());
        
        List<String> sensorIds = Arrays.asList(
            SensorConstants.SensorIds.SOIL_TEMPERATURE,    // 土壤温度
            SensorConstants.SensorIds.SOIL_PH,            // 土壤PH值
            SensorConstants.SensorIds.SOIL_NITROGEN,      // 土壤氮含量
            SensorConstants.SensorIds.SOIL_PHOSPHORUS,    // 土壤磷含量
            SensorConstants.SensorIds.SOIL_POTASSIUM,     // 土壤钾含量
            SensorConstants.SensorIds.AIR_TEMPERATURE,    // 空气温度
            SensorConstants.SensorIds.AIR_HUMIDITY        // 空气相对湿度
        );
        
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.setBearerAuth(apiTokenService.getAccessToken());
        
        HttpEntity<Map<String, List<String>>> request = new HttpEntity<>(
            Map.of("ids", sensorIds),
            headers
        );
        
        String url = SensorConstants.ApiConstants.DATA_URL + "?boxNo=" + SensorConstants.GREENHOUSE_BOX_NO;
        
        try {
            ResponseEntity<List<SensorDataResponse>> response = restTemplate.exchange(
                url,
                HttpMethod.POST,
                request,
                new ParameterizedTypeReference<List<SensorDataResponse>>() {}
            );
            
            log.info("API Response: {}", response.getBody());
            
            if (response.getBody() != null) {
                List<SensorDataResponse> adjustedData = response.getBody().stream()
                    .map(data -> {
                        switch (data.getId()) {
                            case "327061375295689611":  // 空气温度传感器
                            case "327061375295689612":  // 空气相对湿度传感器
                                data.setValue(data.getValue() / 10.0);
                                break;
                        }
                        return data;
                    })
                    .collect(Collectors.toList());
                
                saveSensorData(SensorConstants.GREENHOUSE_BOX_NO, adjustedData);
                return adjustedData;
            }
            
            throw new BusinessException("获取传感器数据失败");
        } catch (Exception e) {
            log.error("调用传感器接口失败", e);
            throw new BusinessException("调用传感器接口失败: " + e.getMessage(), e);
        }
    }
    
    @Transactional
    private void saveSensorData(String boxNo, List<SensorDataResponse> responses) {
        System.out.println("Saving sensor data for boxNo: " + boxNo);
        System.out.println("Responses: " + responses);
        
        GreenHouse greenhouse = getOrCreateGreenhouse(boxNo);
        System.out.println("Greenhouse: " + greenhouse);
        
        responses.forEach(response -> {
            // 根据传感器ID获取或创建设备
            Device device = getOrCreateDevice(response.getId(), greenhouse);
            System.out.println("Device created/found: " + device);
            
            // 不再使用response.getName()覆盖设备名称
            deviceRepository.save(device);
            
            switch (response.getId()) {
                case "327061375295689611": // 空气温度
                    AirTemperatureData airTemp = new AirTemperatureData();
                    airTemp.setDevice(device);
                    airTemp.setValue(response.getValue());
                    airTemp.setCreateTime(LocalDateTime.now());
                    airTemp.setName(device.getDeviceName());  // 使用设备名称
                    airTemp.setUnit("℃");
                    airTemperatureRepository.save(airTemp);
                    break;
                    
                case "327061375295689612": // 空气湿度
                    AirHumidityData airHumidity = new AirHumidityData();
                    airHumidity.setDevice(device);
                    airHumidity.setValue(response.getValue());
                    airHumidity.setCreateTime(LocalDateTime.now());
                    airHumidity.setName(device.getDeviceName());  // 使用设备名称
                    airHumidity.setUnit("%");
                    airHumidityRepository.save(airHumidity);
                    break;
                    
                case "327061375295689614": // 土壤温度
                    SoilTemperatureData soilTemp = new SoilTemperatureData();
                    soilTemp.setDevice(device);
                    soilTemp.setValue(response.getValue());
                    soilTemp.setCreateTime(LocalDateTime.now());
                    soilTemp.setName(device.getDeviceName());  // 使用设备名称
                    soilTemp.setUnit("℃");
                    soilTemperatureRepository.save(soilTemp);
                    break;
                    
                case "327061375295689606": // 土壤PH值
                    SoilPhData soilPh = new SoilPhData();
                    soilPh.setDevice(device);
                    soilPh.setValue(response.getValue());
                    soilPh.setCreateTime(LocalDateTime.now());
                    soilPh.setName(device.getDeviceName());  // 使用设备名称
                    soilPh.setUnit("pH");
                    soilPhRepository.save(soilPh);
                    break;
                    
                case "327061375295689607": // 土壤氮含量
                    SoilNitrogenData soilNitrogen = new SoilNitrogenData();
                    soilNitrogen.setDevice(device);
                    soilNitrogen.setValue(response.getValue());
                    soilNitrogen.setCreateTime(LocalDateTime.now());
                    soilNitrogen.setName(device.getDeviceName());  // 使用设备名称
                    soilNitrogen.setUnit("mg/kg");
                    soilNitrogenRepository.save(soilNitrogen);
                    break;
                    
                case "327061375295689608": // 土壤磷含量
                    SoilPhosphorusData soilPhosphorus = new SoilPhosphorusData();
                    soilPhosphorus.setDevice(device);
                    soilPhosphorus.setValue(response.getValue());
                    soilPhosphorus.setCreateTime(LocalDateTime.now());
                    soilPhosphorus.setName(device.getDeviceName());  // 使用设备名称
                    soilPhosphorus.setUnit("mg/kg");
                    soilPhosphorusRepository.save(soilPhosphorus);
                    break;
                    
                case "327061375295689609": // 土壤钾含量
                    SoilPotassiumData soilPotassium = new SoilPotassiumData();
                    soilPotassium.setDevice(device);
                    soilPotassium.setValue(response.getValue());
                    soilPotassium.setCreateTime(LocalDateTime.now());
                    soilPotassium.setName(device.getDeviceName());  // 使用设备名称
                    soilPotassium.setUnit("mg/kg");
                    soilPotassiumRepository.save(soilPotassium);
                    break;
            }
        });
    }
    
    private Device getOrCreateDevice(String deviceId, GreenHouse greenhouse) {
        return deviceRepository.findByDeviceId(deviceId)
            .orElseGet(() -> {
                Device device = new Device();
                device.setDeviceId(deviceId);
                device.setDeviceName(getDeviceName(deviceId));
                device.setDeviceType(getDeviceType(deviceId));
                device.setGreenhouse(greenhouse);
                device.setSerialNumber(deviceId);
                return deviceRepository.save(device);
            });
    }
    
    private String getDeviceName(String deviceId) {
        switch (deviceId) {
            case "327061375295689611":
                return "空气温度传感器";
            case "327061375295689612":
                return "空气相对湿度传感器";
            case "327061375295689614":
                return "土壤温度传感器";
            case "327061375295689606":
                return "土壤PH值传感器";
            case "327061375295689607":
                return "土壤氮含量传感器";
            case "327061375295689608":
                return "土壤磷含量传感器";
            case "327061375295689609":
                return "土壤钾含量传感器";
            default:
                return "未知传感器";
        }
    }
    
    private String getDeviceType(String deviceId) {
        switch (deviceId) {
            case "327061375295689611":
                return "AIR_TEMPERATURE";
            case "327061375295689612":
                return "AIR_HUMIDITY";
            case "327061375295689614":
                return "SOIL_TEMPERATURE";
            case "327061375295689606":
                return "SOIL_PH";
            case "327061375295689607":
                return "SOIL_NITROGEN";
            case "327061375295689608":
                return "SOIL_PHOSPHORUS";
            case "327061375295689609":
                return "SOIL_POTASSIUM";
            default:
                return "UNKNOWN";
        }
    }
    
    private GreenHouse getOrCreateGreenhouse(String boxNo) {
        return greenhouseRepository.findByBoxNo(boxNo)
            .orElseGet(() -> {
                GreenHouse greenhouse = new GreenHouse();
                greenhouse.setBoxNo(SensorConstants.GREENHOUSE_BOX_NO);  // 使用常量值
                greenhouse.setName("智慧大棚");  // 设置默认名称
                greenhouse.setArea(5.65);  // 设置默认面积
                greenhouse.setAddress("云南昆明");  // 设置默认地址
                return greenhouseRepository.save(greenhouse);
            });
    }
    
    /**
     * 获取指定时间范围内的所有传感器数据
     */
    public List<Map<String, Object>> getAllDataByTimeRange(
            LocalDateTime startTime, 
            LocalDateTime endTime) {
        List<Map<String, Object>> result = new ArrayList<>();
        
        airTemperatureRepository.findByCreateTimeBetween(startTime, endTime)
            .forEach(data -> result.add(convertToUnifiedResponse(data)));
        
        airHumidityRepository.findByCreateTimeBetween(startTime, endTime)
            .forEach(data -> result.add(convertToUnifiedResponse(data)));
        
        soilTemperatureRepository.findByCreateTimeBetween(startTime, endTime)
            .forEach(data -> result.add(convertToUnifiedResponse(data)));
        
        soilPhRepository.findByCreateTimeBetween(startTime, endTime)
            .forEach(data -> result.add(convertToUnifiedResponse(data)));
        
        soilNitrogenRepository.findByCreateTimeBetween(startTime, endTime)
            .forEach(data -> result.add(convertToUnifiedResponse(data)));
        
        soilPhosphorusRepository.findByCreateTimeBetween(startTime, endTime)
            .forEach(data -> result.add(convertToUnifiedResponse(data)));
        
        soilPotassiumRepository.findByCreateTimeBetween(startTime, endTime)
            .forEach(data -> result.add(convertToUnifiedResponse(data)));
        
        return result;
    }
    
    private <T extends BaseSensorData> Map<String, Object> convertToUnifiedResponse(T data) {
        System.out.println("Converting data: " + data);  // 添加日志
        System.out.println("Device: " + data.getDevice());  // 添加日志
        
        Map<String, Object> response = new HashMap<>();
        if (data.getDevice() != null) {
            response.put("id", data.getDevice().getDeviceId());
        } else {
            System.out.println("Warning: Device is null for data: " + data);  // 添加警告日志
        }
        response.put("name", data.getName());
        response.put("value", data.getValue());
        response.put("timestamp", data.getCreateTime());
        return response;
    }
    
    /**
     * 获取指定时间点的所有传感器数据
     */
    public List<Map<String, Object>> getDataByTime(LocalDateTime timestamp) {
        List<Map<String, Object>> result = new ArrayList<>();
        
        // 查询每个传感器在指定时间点最接近的数据
        airTemperatureRepository.findByCreateTimeBetween(timestamp.minusMinutes(5), timestamp.plusMinutes(5))
            .stream()
            .findFirst()
            .ifPresent(data -> result.add(convertToUnifiedResponse(data)));
        
        airHumidityRepository.findByCreateTimeBetween(timestamp.minusMinutes(5), timestamp.plusMinutes(5))
            .stream()
            .findFirst()
            .ifPresent(data -> result.add(convertToUnifiedResponse(data)));
        
        soilTemperatureRepository.findByCreateTimeBetween(timestamp.minusMinutes(5), timestamp.plusMinutes(5))
            .stream()
            .findFirst()
            .ifPresent(data -> result.add(convertToUnifiedResponse(data)));
        
        soilPhRepository.findByCreateTimeBetween(timestamp.minusMinutes(5), timestamp.plusMinutes(5))
            .stream()
            .findFirst()
            .ifPresent(data -> result.add(convertToUnifiedResponse(data)));
        
        soilNitrogenRepository.findByCreateTimeBetween(timestamp.minusMinutes(5), timestamp.plusMinutes(5))
            .stream()
            .findFirst()
            .ifPresent(data -> result.add(convertToUnifiedResponse(data)));
        
        soilPhosphorusRepository.findByCreateTimeBetween(timestamp.minusMinutes(5), timestamp.plusMinutes(5))
            .stream()
            .findFirst()
            .ifPresent(data -> result.add(convertToUnifiedResponse(data)));
        
        soilPotassiumRepository.findByCreateTimeBetween(timestamp.minusMinutes(5), timestamp.plusMinutes(5))
            .stream()
            .findFirst()
            .ifPresent(data -> result.add(convertToUnifiedResponse(data)));
        
        return result;
    }
}
